home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume8 / unpkmaps2 < prev    next >
Encoding:
Text File  |  1989-09-23  |  18.0 KB  |  563 lines

  1. Newsgroups: comp.sources.misc
  2. subject: v08i047: secure comp.mail.maps unpacker
  3. from: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  4. Reply-To: clewis@eci386 (Chris Lewis)
  5.  
  6. Posting-number: Volume 8, Issue 47
  7. Submitted-by: clewis@eci386 (Chris Lewis)
  8. Archive-name: unpkmaps2
  9.  
  10. Periodically, people ask for a map unpacker.  I've posted this
  11. before in news.admin, but I guess it's time to make it semi-official.
  12. The one big advantage to this one is that it's intended to be secure,
  13. and is in fact somewhat better than uuhosts (not to mention a little
  14. less of a CPU and disk hog).  It's been in use for about 8 months
  15. on a couple of machines.
  16.  
  17. I call it "unpackmaps V2.0"
  18.  
  19. #! /bin/sh
  20. # This is a shell archive.  Remove anything before this line, then unpack
  21. # it by saving it into a file and typing "sh file".  To overwrite existing
  22. # files, type "sh file -c".  You can also feed this as standard input via
  23. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  24. # will see the following message at the end:
  25. #        "End of archive 1 (of 1)."
  26. # Contents:  MANIFEST README path.patch unpackmaps
  27. # Wrapped by clewis@eci386 on Tue Sep 19 15:27:00 1989
  28. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  29. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  30.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  31. else
  32. echo shar: Extracting \"'MANIFEST'\" \(275 characters\)
  33. sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  34. X   File Name        Archive #    Description
  35. X-----------------------------------------------------------
  36. X MANIFEST                   1    This shipping list
  37. X README                     1    Readme
  38. X path.patch                 1    patch for pathalias
  39. X unpackmaps                 1    map unpacker
  40. END_OF_FILE
  41. if test 275 -ne `wc -c <'MANIFEST'`; then
  42.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  43. fi
  44. # end of 'MANIFEST'
  45. fi
  46. if test -f 'README' -a "${1}" != "-c" ; then 
  47.   echo shar: Will not clobber existing file \"'README'\"
  48. else
  49. echo shar: Extracting \"'README'\" \(6341 characters\)
  50. sed "s/^X//" >'README' <<'END_OF_FILE'
  51. X            UNPACKMAPS V2.0
  52. X
  53. XThis is source for a simple, *secure*, map unpacking facility.
  54. X
  55. XIt is much simpler than uuhosts, though it will grow somewhat.
  56. XThe intent is that any UNIX/XENIX/BSD system that can run news will
  57. Xbe able to run this too, so I'm attempting to keep to greatest-common-
  58. Xdenominator except for pathalias and things that I implement
  59. Xmyself.
  60. X
  61. XThis release includes a patch to pathalias 9 to allow pathalias to
  62. Xhandle compressed files in its argument list.  You don't need to
  63. Xpatch pathalias, but if you don't, you can't keep the maps in compressed
  64. Xformat (yet).
  65. X
  66. XThe next release (if there ever is one) will contain such things as 
  67. Xmore efficient path file modification, a method for viewing the map 
  68. Xfiles analogously to uuhosts (from which this software derives a 
  69. Xconsiderable amount of inspiration), and more automated installation.
  70. X
  71. XA slightly older version of this was mailed to Neil Gorsuch (of Zardoz
  72. Xsecurity mailing list fame) who said that he would be working on it,
  73. Xpossibly merging it with other map unpackers and officially posting it.
  74. XI'm posting it because I occasionally see requests for it, and Neil
  75. Xhasn't posted his yet.
  76. X
  77. XRegarding security: as many may remember, there's been a fair bit of
  78. Xdiscussion on security of map unpacking on the net.  Rather than play
  79. Xaround with trying to make a secure *true* unshar, which probably noone would
  80. Xtrust because it would be so big, I simply made a few simplifying assumptions
  81. Xabout the map format and use an awk script to unpack a map article into
  82. Xa map file.  It checks for and refuses to unpack articles which have
  83. Xslashes in their names.  I sent off some mail to Mel asking whether the
  84. Xassumptions I've made about map format are true, but never got any
  85. Xresponse.  I believe that this is *pretty* secure, in that it doesn't
  86. Xhave to be run as root, doesn't use the Bourne shell for unpacking, and 
  87. Xis careful about the file names it creates.  Please let me know if there 
  88. Xare any holes I didn't think of.
  89. X
  90. XGeneral operation:
  91. X    - your news is modified to batch incoming map article file names
  92. X      to a specific batch file (analogous to normal news batching).  
  93. X      C-news users take note: you may have to utter magic incantations 
  94. X      (hint "classes" in C-news Alpha) to get sendbatches to avoid 
  95. X      trying to uux these...
  96. X    - unpackmaps wakes up, usually once per day, and extracts the
  97. X      maps specified (if any) in the batch file into the map directory.
  98. X      Maps are extracted using a secure awk script without resort
  99. X      to setuid root or other wierdnesses.
  100. X    - If any maps were extracted, pathalias is fired off, and the 
  101. X      resultant file put in the place specified.
  102. X    - if anything was done, unpackmaps sends you mail telling you
  103. X      what happened.
  104. X
  105. XInstallation:
  106. X    - If you want to compress the stored map files, patch your
  107. X      version 9 pathalias source using the path.patch file.  DO NOT 
  108. X      define COMPRESS in unpackmaps unless you've done so.
  109. X      All this patch does is allow pathalias to handle files arguments
  110. X      that may be compressed - it does it by "system"'ing off COMPRESS when
  111. X      files with ".Z" are found.  (I wanted to "popen" COMPRESS, but
  112. X      the changes looked a little extreme, besides, having both
  113. X      compress and pathalias running on a pipeline seemed to me to
  114. X      be a little extreme w.r.t. memory.)  You should, after patching
  115. X      pathalias, modify the COMPRESS define in pathalias's config.h
  116. X      to contain the same pathname as the COMPRESS in unpackmaps,
  117. X      except that the COMPRESS define in config.h should also include
  118. X      "-d" (it's being used as if it was zcat)
  119. X
  120. X      uuhosts does compression by completely uncompressing the map
  121. X      database, and running pathalias, and then recompressing the whole
  122. X      thing again.  Which means you need an enormous amount of disk
  123. X      (approx 3 times the compressed map directory), plus be willing
  124. X      to put up with a compression run on every map file too.  (not
  125. X      to mention intermediate path files)
  126. X
  127. X      My approach only needs enough space for one additional uncompressed
  128. X      map entry plus a copy or two of the final map database.
  129. X
  130. X    - I STRONGLY recommend that you build a special version of compress
  131. X      with 12 bit compression instead of 16 for this.  When this
  132. X      is done, compress is considerably smaller (eg: bss of 32K instead
  133. X      of 400K+).  The reason for this is obvious - pathalias is enormous
  134. X      when it's running, and it ain't nice to have to fork/exec
  135. X      a 500K+ process at the same time for *every* map file.  Ugh.
  136. X
  137. X      Advantages:
  138. X        - the whole thing runs considerably faster
  139. X        - much less swap/paging
  140. X        - on our machine, 16 bit compress practically hangs
  141. X          everyone else when run at the same time as pathalias.
  142. X      Disadvantages:
  143. X        - the map directory is 10% (really!  only 10%!) bigger.
  144. X
  145. X      What I did was the following:
  146. X        - go to the source directory for compress
  147. X        - remove the binary if it is there.
  148. X        - say:
  149. X            make compress CFLAGS=-DUSERMEM=0
  150. X        - rename this to something like /usr/bin/compress12
  151. X        - make sure that the config.h for pathalias and unpackmaps
  152. X          shell script has the same name.  Eg: /usr/bin/compress12
  153. X
  154. X    - edit unpackmaps to set the variables at the beginning of
  155. X      the shell script.
  156. X    - make the directory for the map files, owned by news.
  157. X    - put unpackmaps in a suitable place.  Eg: /usr/lib/news
  158. X    - unpackmaps should be run from the userid that owns and runs news.
  159. X    - su to the news userid, and run:
  160. X        unpackmaps -i
  161. X      This will build the initial path file.
  162. X    - insert into your crontab something like:
  163. X        30 3 * * * /bin/su news -c "<path to unpackmaps>/unpackmaps > /dev/null"
  164. X
  165. Xunpackmaps -i: will extract all map articles into the map spool area - useful
  166. Xfor the first time you use it.
  167. X
  168. Xunpackmaps -p: runs pathalias even if no map articles were extracted.
  169. X
  170. XIn order to push the paths file into /usr/lib/uucp, I created a file called
  171. Xpaths in /usr/lib/uucp, with 644 permissions, owned by the userid that runs
  172. Xunpackmaps.
  173. X
  174. XThe package will send mail to who you specify indicating which maps were
  175. Xunpacked, and any error returns from pathalias.
  176. X
  177. XLet me know of any changes you needed to make to get this to work.
  178. XI'm also open to suggestions for new features....
  179. X----------
  180. XChris Lewis, Markham, Ontario, Canada
  181. X{uunet!attcan,utgpu,yunexus,utzoo}!lsuc!{ecicrl|eci386}!clewis
  182. XFerret Mailing list: ...!lsuc!gate!eci386!ferret-request
  183. X(or lsuc!gate!eci386!clewis or lsuc!clewis)
  184. END_OF_FILE
  185. if test 6341 -ne `wc -c <'README'`; then
  186.     echo shar: \"'README'\" unpacked with wrong size!
  187. fi
  188. # end of 'README'
  189. fi
  190. if test -f 'path.patch' -a "${1}" != "-c" ; then 
  191.   echo shar: Will not clobber existing file \"'path.patch'\"
  192. else
  193. echo shar: Extracting \"'path.patch'\" \(2225 characters\)
  194. sed "s/^X//" >'path.patch' <<'END_OF_FILE'
  195. X*** config.old.h
  196. X--- config.h
  197. X**************
  198. X*** 11,16
  199. X  /* default place for dbm output of makedb (or use -o at run-time) */
  200. X  #define    ALIASDB    "/usr/local/lib/palias"
  201. X  
  202. X  
  203. X  
  204. X  /**************************************************************************
  205. X--- 11,19 -----
  206. X  /* default place for dbm output of makedb (or use -o at run-time) */
  207. X  #define    ALIASDB    "/usr/local/lib/palias"
  208. X  
  209. X+ /* place for zcat for uncompressing maps inline */
  210. X+ #define    COMPRESS    "/usr2/clewis/maps/compress12 -d"
  211. X+ 
  212. X  
  213. X  
  214. X  /**************************************************************************
  215. X*** parse.old.y
  216. X--- parse.y
  217. X**************
  218. X*** 402,407
  219. X      fixprivate();    /* munge private host definitions */
  220. X      Lineno = 1;
  221. X      while (optind < Argc) {
  222. X          if (freopen((Cfile = Argv[optind++]), "r", stdin) != 0)
  223. X              return 0;
  224. X          sprintf(errbuf, "%s: %s", Argv[0], Cfile);
  225. X--- 402,439 -----
  226. X      fixprivate();    /* munge private host definitions */
  227. X      Lineno = 1;
  228. X      while (optind < Argc) {
  229. X+ #ifdef    COMPRESS
  230. X+         static char Fname[512];
  231. X+         char compressed[512];
  232. X+         char cmdbuf[512];
  233. X+         int dotidx;
  234. X+ 
  235. X+         strcpy(Fname, Cfile = Argv[optind++]);
  236. X+         strcpy(compressed, Cfile);
  237. X+ 
  238. X+         dotidx = strlen(Cfile) - 2;
  239. X+         if (dotidx >= 0 && strcmp(&Fname[dotidx], ".Z") == 0)
  240. X+             Fname[dotidx] = '\0';
  241. X+         else
  242. X+             strcat(compressed, ".Z");
  243. X+ 
  244. X+         if (access(Fname, 04) != 0) {
  245. X+             if (access(compressed, 04) == 0) {
  246. X+                 unlink("/tmp/pathfile");
  247. X+                 sprintf(cmdbuf, "%s < %s > /tmp/pathfile", COMPRESS,
  248. X+                     compressed);
  249. X+                 system(cmdbuf);
  250. X+                 if (freopen("/tmp/pathfile", "r", stdin) != 0) {
  251. X+                     unlink("/tmp/pathfile");
  252. X+                     return 0;
  253. X+                 }
  254. X+                 unlink("/tmp/pathfile");
  255. X+             }
  256. X+         } else {
  257. X+             if (freopen(Fname, "r", stdin) != 0)
  258. X+                 return 0;
  259. X+         }
  260. X+ #else
  261. X          if (freopen((Cfile = Argv[optind++]), "r", stdin) != 0)
  262. X              return 0;
  263. X  #endif
  264. X**************
  265. X*** 404,409
  266. X      while (optind < Argc) {
  267. X          if (freopen((Cfile = Argv[optind++]), "r", stdin) != 0)
  268. X              return 0;
  269. X          sprintf(errbuf, "%s: %s", Argv[0], Cfile);
  270. X          perror(errbuf);
  271. X      }
  272. X--- 436,442 -----
  273. X  #else
  274. X          if (freopen((Cfile = Argv[optind++]), "r", stdin) != 0)
  275. X              return 0;
  276. X+ #endif
  277. X          sprintf(errbuf, "%s: %s", Argv[0], Cfile);
  278. X          perror(errbuf);
  279. X      }
  280. END_OF_FILE
  281. if test 2225 -ne `wc -c <'path.patch'`; then
  282.     echo shar: \"'path.patch'\" unpacked with wrong size!
  283. fi
  284. # end of 'path.patch'
  285. fi
  286. if test -f 'unpackmaps' -a "${1}" != "-c" ; then 
  287.   echo shar: Will not clobber existing file \"'unpackmaps'\"
  288. else
  289. echo shar: Extracting \"'unpackmaps'\" \(5874 characters\)
  290. sed "s/^X//" >'unpackmaps' <<'END_OF_FILE'
  291. X#    Written by: Chris Lewis, clewis@eci386
  292. X#    unpackmaps release 2.0 (alpha)
  293. Xtrap "rm -f /tmp/unp?$$; exit" 0 1 2 3 15
  294. XIFS="     
  295. X"
  296. Xexport IFS
  297. XPATH=/bin:/usr/bin
  298. Xexport PATH
  299. X
  300. X#    The name of the file that you've caused your news system to
  301. X#    batch the file names of the map articles.
  302. X# Eg: C-news
  303. XBATCH=/usr/lib/news/batch/b.maps/togo
  304. X# Eg: B-news
  305. X#BATCH=/usr/spool/batch/maps
  306. X#    News spool directory
  307. XNEWSSPOOL=/usr/spool/news
  308. X#    Where you want the maps to go.
  309. X#    I like using /usr/spool/maps, but on our system, /usr/spool/news
  310. X#    is a separate file system, and /usr runs close to the limit...
  311. XMAPDIR=/usr/spool/news/maps
  312. X#    Person to send results and error messages to
  313. XNOTIFY=clewis
  314. X#    pathalias binary
  315. XPATHALIAS=/usr/lbin/pathalias
  316. X#    where you want the path files to go:
  317. X#    A convenient place is /usr/lib/uucp/paths which is the smail
  318. X#    default.  If you're going to put this in /usr/lib/uucp, I suggest
  319. X#    (rather than make /usr/lib/uucp writeable by everybody), doing
  320. X#    the following:
  321. X#        su root
  322. X#        cd /usr/lib/uucp
  323. X#        touch paths
  324. X#        chown news paths    (or usenet)
  325. X#        chmod 644 paths
  326. XPATHFILE=/usr/lib/uucp/paths
  327. X#    Auxiliary options to pathalias.  Tune to local tastes....
  328. XPATHOPTS="-dwatmath"
  329. X#    If you have a version[s] of your machine's map entry that is different 
  330. X#    from what's published, change this variable to point at it/them.
  331. X#    (Eg: I publish the first entry here, and the second one is local tuning
  332. X#    and hidden connections)
  333. XPATHLOCAL="/usr2/clewis/maps/path.local /usr2/clewis/maps/path.nonpublic"
  334. X#    If this variable is set to the compress binary, maps will be
  335. X#    compressed.  DO NOT define this unless you've applied the accompanying
  336. X#    patch to your version 9 pathalias source, and you've set the path
  337. X#    in config.h to point at the same place.
  338. XCOMPRESS=/usr2/clewis/maps/compress12
  339. X#    1 to strip comments from maps - don't do this if you ever try
  340. X#    to read the maps and figure out where the site is, their contacts
  341. X#    etc.
  342. XNOCOMMENTS=0
  343. X#    PS: there is *one* possible edit that you might want to make
  344. X#    below - the maps used to generate wierd domains, but most of that
  345. X#    appears to be gone now (don't ask me, I never particularly understood
  346. X#    it, but since Peter Honeyman recommended it...).  If you object
  347. X#    to these wierd domains, uncomment the egrep.
  348. X
  349. X#    Edit no more....
  350. X
  351. Xumask 022
  352. X
  353. XPATH=/bin:/usr/bin
  354. Xexport PATH
  355. X
  356. Xif test ! -d $MAPDIR -o ! -w $MAPDIR
  357. Xthen
  358. X    echo "$MAPDIR missing, unwritable or not a directory" >&2
  359. X    exit
  360. Xfi
  361. X
  362. Xif [ $# = 1 ]
  363. Xthen
  364. X    case $1 in
  365. X    -p)
  366. X        forcepath=true
  367. X        ;;
  368. X    -i)
  369. X        cd /
  370. X        rm -f $BATCH.work
  371. X        # using find/sort instead of ls just in case there's lots of
  372. X        # articles....
  373. X        find $NEWSSPOOL/comp/mail/maps -type f -print | sort > $BATCH
  374. X        ;;
  375. X    *)
  376. X        echo "usage: unpackmaps [-i] [-p]" >&2
  377. X        exit 1
  378. X        ;;
  379. X    esac
  380. Xfi
  381. X
  382. Xcd $MAPDIR
  383. X        
  384. Xwhile [ -f $BATCH -o -f $BATCH.work ]
  385. Xdo
  386. X    # There is no window of vulnerability here as long as noone else is
  387. X    # creating $BATCH.work.
  388. X    if [ ! -f $BATCH.work ]
  389. X    then
  390. X    mv $BATCH $BATCH.work
  391. X    fi
  392. X
  393. X    while read i stuff
  394. X    do
  395. X    #    Using stuff to capture remaining junk on line.
  396. X    #    Eg: C-news article sizes.
  397. X
  398. X    if [ -z "$i" ]
  399. X    then
  400. X        break
  401. X    fi
  402. X
  403. X    if [ ! -r $i ]
  404. X    then
  405. X        echo "$i apparently superseded or expired"
  406. X        continue
  407. X    fi
  408. X
  409. X    # This awk script depends on the following map article format:
  410. X    # <don't cares>
  411. X    # cat << 'something' > filename
  412. X    # map body
  413. X    # something
  414. X    # <don't cares>
  415. X    # "something" doesn't have to be enclosed in quotes in the cat line.
  416. X    # This isn't particularly fast - could be dramatically speeded up
  417. X    # if written in C, but I was trying to ensure that this is as simple
  418. X    # and self-evident as possible.
  419. X
  420. X    awk '
  421. X    $1 == "cat" {
  422. X        endtoken=$3;
  423. X        if (substr(endtoken, 1, 1) == "'"'"'")
  424. X            endtoken=substr(endtoken, 2, length(endtoken)-2);
  425. X        collecting = 1;
  426. X        foundone = 1;
  427. X        name = $5;
  428. X        if (index(name, "/") != 0) {
  429. X            printf("Security violation attempt in %s!\n", "'$i'");
  430. X            exit;
  431. X        } else
  432. X            printf("extracting %s from %s\n", name, "'$i'");
  433. X        next;
  434. X        }
  435. X
  436. X        {
  437. X        if (!collecting)
  438. X            next;
  439. X        if ($1 == endtoken) {
  440. X            line = "rm -f " name ".Z"
  441. X            print "" | line
  442. X            collecting = 0;
  443. X            next
  444. X        }
  445. X        if ("'$NOCOMMENTS'" == 1 && $0 ~ /#/)
  446. X            print substr($0, 1, index($0, "#")) > name
  447. X        else
  448. X            print $0 > name
  449. X        }
  450. X        
  451. X        END {
  452. X        if (collecting) {
  453. X            printf("Non-terminated map in %s\n", "'$i'");
  454. X        }
  455. X        if (!foundone) {
  456. X            printf("%s does not contain a properly formed map\n", "'$i'");
  457. X        }
  458. X        }' $i
  459. X    done < $BATCH.work
  460. X    rm $BATCH.work
  461. Xdone > /tmp/unpA$$ 2>&1
  462. X
  463. Xif test -n "$PATHALIAS" -a -x "$PATHALIAS" 
  464. Xthen
  465. X    if test -s /tmp/unpA$$ -o -n "$forcepath"
  466. X    then
  467. X    cd $MAPDIR
  468. X
  469. X    (
  470. X    $PATHALIAS -f $PATHOPTS ?.* $PATHLOCAL |
  471. X
  472. X    # format of the pathalias -f output is
  473. X    # cost    host    route
  474. X    #
  475. X    # format of a 'paths' file for smail is
  476. X    # host    route    first_hop_cost
  477. X    #
  478. X    # move cost field to end of line:
  479. X
  480. X    sed 's/\(.*\)    \(.*\)    \(.*\)/\2    \3    \1/' |
  481. X
  482. X    # convert target domain/host to lower case:
  483. X
  484. X    #lcasep |
  485. X    
  486. X    # remove some additional wierdnesses (per Peter Honeyman):
  487. X    # You can leave it in or not.
  488. X
  489. X    # egrep -v '(\.(com|edu|mil|gov|net|org|arpa|[a-z][a-z])    .*!.*!)|(.\.(com|edu|mil|gov|net|org|arpa|[a-z][a-z])    )' |
  490. X
  491. X    # sort the stream:
  492. X    
  493. X    sort > /tmp/paths ) > /tmp/unpB$$ 2>&1
  494. X
  495. X    if test ! -s /tmp/paths
  496. X    then
  497. X        echo "Pathalias failed no map file created" >> /tmp/unpB$$
  498. X    else
  499. X        cat /tmp/paths > $PATHFILE 2>> /tmp/unpB$$
  500. X        if test $? != 0
  501. X        then
  502. X        echo "Copy to $PATHFILE failed" >> /tmp/unpB$$
  503. X        else
  504. X        rm /tmp/paths
  505. X        fi
  506. X        echo "Map remade" >> /tmp/unpB$$
  507. X    fi
  508. X
  509. X    if test -s /tmp/unpB$$
  510. X    then
  511. X        echo "Pathalias output:" >> /tmp/unpA$$
  512. X        cat /tmp/unpB$$ >> /tmp/unpA$$
  513. X    fi
  514. X    fi
  515. Xfi
  516. X
  517. Xif test -x "$COMPRESS"
  518. Xthen
  519. X    files=`ls ?.* | sed -e '/\.Z$/d'`
  520. X    if [ -n "$files" ]
  521. X    then
  522. X    $COMPRESS -f $files
  523. X    fi
  524. Xfi
  525. X
  526. X
  527. Xif test -s /tmp/unpA$$
  528. Xthen
  529. X    mail $NOTIFY < /tmp/unpA$$
  530. Xfi
  531. END_OF_FILE
  532. if test 5874 -ne `wc -c <'unpackmaps'`; then
  533.     echo shar: \"'unpackmaps'\" unpacked with wrong size!
  534. fi
  535. chmod +x 'unpackmaps'
  536. # end of 'unpackmaps'
  537. fi
  538. echo shar: End of archive 1 \(of 1\).
  539. cp /dev/null ark1isdone
  540. MISSING=""
  541. for I in 1 ; do
  542.     if test ! -f ark${I}isdone ; then
  543.     MISSING="${MISSING} ${I}"
  544.     fi
  545. done
  546. if test "${MISSING}" = "" ; then
  547.     echo You have the archive.
  548.     echo "Read README to install unpackmaps"
  549.     rm -f ark[1-9]isdone
  550. else
  551.     echo You still need to unpack the following archives:
  552.     echo "        " ${MISSING}
  553. fi
  554. ##  End of shell archive.
  555. exit 0
  556.  
  557. -- 
  558. Chris Lewis, R.H. Lathwell & Associates: Elegant Communications Inc.
  559. UUCP: {uunet!mnetor, utcsri!utzoo}!lsuc!eci386!clewis
  560. Phone: (416)-595-5425
  561.  
  562.  
  563.